/////////////////////
// lfw picture database parser.
// This is executable. (may be used as library as well.)
// Author: seojin
// Email: seojin@mit.edu
////////////////////////
#include <Tracker.h>
#include <opencv/highgui.h>
#include <iostream>
//[Modified]:seojin
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <vector>
#include <string>
#include <fstream>
using namespace std;

unsigned int processCount = 0;

//By Jen
//TODO: link to jen's class
std::vector<double> generateVector(cv::Mat &shape, cv::Mat &visi) {
    //draw points
    int n = shape.rows/2;
    std::vector<double> feature_vector;
    for(int i = 0; i < n; i++){
        if(visi.at<int>(i,0) == 0)continue;
        feature_vector.push_back(shape.at<double>(i,0));
        feature_vector.push_back(shape.at<double>(i+n,0));
    }

    double x_min = 10e16;
    double x_max = 10e-16;
    double y_min = 10e16;
    double y_max = 10e-16;

    for (int i = 0; i < feature_vector.size(); i += 2) {
        if (feature_vector[i] < x_min) {
            x_min = feature_vector[i];
        }
        if (feature_vector[i] > x_max) {
            x_max = feature_vector[i];
        }
        if (feature_vector[i+1] < y_min) {
            y_min = feature_vector[i+1];
        }
        if (feature_vector[i+1] > y_max) {
            y_max = feature_vector[i+1];
        }
    }

    for (int i = 0; i < feature_vector.size(); i++) {
        if (i % 2 == 0) {
            feature_vector[i] = feature_vector[i] - (x_min + x_max)/2;
        } else {
            feature_vector[i] = feature_vector[i] - (y_min + y_max)/2;
        }
    }

    if ((x_max - (x_min+x_max)/2) > (y_max - (y_min+y_max)/2)) {
        for (int i = 0; i < feature_vector.size(); i++) {
            feature_vector[i] = feature_vector[i] / (x_max - (x_min+x_max)/2);
        }
    } else {
        for (int i = 0; i < feature_vector.size(); i++) {
            feature_vector[i] = feature_vector[i] / (y_max - (y_min+y_max)/2);
        }
    }
    return feature_vector;
}

//By seojin
void printFV(std::vector<double> fv, ofstream &output){
  int i;
  for( i = 0; i < fv.size(); i++){
  //  std::cout<< fv[i] << " ";
    output <<  " " << fv[i];
  }
}

/*function... might want it in some class?*/
int getdir (string dir, vector<string> &files)
{
    DIR *dp;
    struct dirent *dirp;
    if((dp  = opendir(dir.c_str())) == NULL) {
        cout << "Error(" << errno << ") opening " << dir << endl;
        return errno;
    }

    while ((dirp = readdir(dp)) != NULL) {
        files.push_back(string(dirp->d_name));
    }
    closedir(dp);
    return 0;
}

bool hasEnding (std::string const &fullString, std::string const &ending)
{
    if (fullString.length() >= ending.length()) {
        return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
    } else {
        return false;
    }
}

void parsePicToFeatureVectors(string path, ofstream &output){
    processCount++;
    if(processCount % 10 == 0) cout << processCount << " " << processCount / 180 << endl;
//    cout << "Starting pic2FV with "<< endl << "   " << path << endl;
    //parse command line arguments
    char ftFile[256],conFile[256],triFile[256];
    strcpy(ftFile,"../model/face2.tracker");
    strcpy(conFile,"../model/face.con");
    strcpy(triFile,"../model/face.tri");
    bool fcheck = false; double scale = 1; int fpd = -1; bool show = true;

    //set other tracking parameters
    std::vector<int> wSize1(1); wSize1[0] = 7;
    std::vector<int> wSize2(3); wSize2[0] = 11; wSize2[1] = 9; wSize2[2] = 7;
    int nIter = 5; double clamp=3,fTol=0.01; 

    FACETRACKER::Tracker model(ftFile);
    cv::Mat tri=FACETRACKER::IO::LoadTri(triFile);
    cv::Mat con=FACETRACKER::IO::LoadCon(conFile);

    cv::Mat frame,gray,im; double fps=0; char sss[256]; std::string text; 
    bool failed = true;


    IplImage* I = cvLoadImage(path.c_str()); frame = I;
    if(scale == 1)im = frame; 
    else cv::resize(frame,im,cv::Size(scale*frame.cols,scale*frame.rows));
    cv::flip(im,im,1); cv::cvtColor(im,gray,CV_BGR2GRAY);


    //track this image
    std::vector<int> wSize; if(failed)wSize = wSize2; else wSize = wSize1; 
    if(model.Track(gray,wSize,fpd,nIter,clamp,fTol,fcheck) == 0){
      int idx = model._clm.GetViewIdx(); failed = false;
      std::vector<double> fv = generateVector(model._shape, model._clm._visi[idx]);
      printFV(fv, output);
    }
}

int parseDir(string dir, ofstream &output){
    vector<string> subdirs = vector<string>();

    getdir(dir,subdirs);

    for (unsigned int i = 0;i < subdirs.size();i++) {
        if(subdirs[i] == "." || subdirs[i] == "..") continue;
        //cout << subdirs[i] << endl;
        vector<string> files = vector<string>();
        getdir(dir + "/" + subdirs[i], files);
        for(unsigned int j = 0; j < files.size(); j++){
            if(hasEnding(files[j], ".jpg")){
                //cout << "   " << files[j] << endl;
                string picPath = dir + "/" + subdirs[i] + "/" + files[j];
                output << files[j] << ":";
                parsePicToFeatureVectors(picPath, output);
                output << endl;
            }
        }
    }
    return 0;
}

int generateTrainingSet(string database_directory, string output_filepath ){
   ofstream output;
   output.open(output_filepath.c_str());
   return parseDir(database_directory, output);
}

//=============================================================================
int main(int argc, const char** argv)
{
  return generateTrainingSet(string("lfw"), string("../db/lfw"));
}
//=============================================================================
